using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;

namespace Controls
{
    public enum Direction { HORIZONTAL, VERTICAL }

    /// <summary>
    /// Implements a Slider Control
    /// </summary>
    public class Slider : Microsoft.SPOT.Presentation.Controls.Control
    {
        public delegate void ValueChangedHandler(object sender, int newValue);
        /// <summary>
        /// Triggered whenever Value is changed
        /// </summary>
        public event ValueChangedHandler ValueChanged;

        Color m_slider_color = Colors.Gray;
        SolidColorBrush brush = new SolidColorBrush(Colors.Red);
        Pen pen = new Pen(Colors.Green);

        const int MARGIN = 2;

        int m_max = 100;
        int m_min = 0;

        ushort m_slider_size = 10;
        int m_value = 0;
        readonly Direction DIRECTION;

        /// <summary>
        /// Default constuctor. Creates a Vertical slider.
        /// </summary>
        public Slider() : this(Direction.VERTICAL) { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="dir">Direction of slider.</param>
        public Slider(Direction dir)
        {
            DIRECTION = dir;
            switch (DIRECTION)
            {
                case Direction.VERTICAL:
                    Width = 30;
                    Height = 100;
                    break;
                case Direction.HORIZONTAL:
                    Width = 100;
                    Height = 30;
                    break;
            }
        }

        /// <summary>
        /// Gets and sets maximum value
        /// </summary>
        public int MaxValue
        {
            get
            {
                return m_max;
            }
            set
            {
                if (value > m_min && value >= Value)
                {
                    m_max = value;
                    Invalidate();
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Gets and sets minimum value
        /// </summary>
        public int MinValue
        {
            get
            {
                return m_min;
            }
            set
            {
                if (value < m_max && value <= Value)
                {
                    m_min = value;
                    Invalidate();
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Gets or sets value of Slider control
        /// </summary>
        public int Value
        {
            get
            {
                return m_value;
            }
            set
            {
                if (value >= m_min && value <= m_max)
                {
                    if (value != m_value)
                    {
                        m_value = value;
                        Invalidate();
                        if (ValueChanged != null)
                        {
                            ValueChanged(this, m_value);
                        }
                    }
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Sets size of slider "button". Sets width if Direction is horizontal, and height otherwise.
        /// </summary>
        public ushort SliderSize
        {
            get
            {
                return m_slider_size;
            }
            set
            {
                m_slider_size = value;
                Invalidate();
            }
        }

        /// <summary>
        /// Inherited. Draws the slider on the DrawingContext that is provided.
        /// </summary>
        /// <param name="dc">DrawingContext to draw upon.</param>
        public override void OnRender(Microsoft.SPOT.Presentation.Media.DrawingContext dc)
        {
            base.OnRender(dc);

            int range, pos;

            switch (DIRECTION)
            {
                case Direction.VERTICAL:
                    // Actual range of possible positions for slider
                    range = ActualHeight - (2 * MARGIN) - m_slider_size;
                    // Find position
                    pos = range - ((m_value - m_min) * range / (m_max - m_min)) + MARGIN;
                    // Draw the slider button
                    dc.DrawRectangle(brush, pen, MARGIN, pos, ActualWidth - (2 * MARGIN), m_slider_size);
                    break;
                case Direction.HORIZONTAL:
                    // Actual range of possible positions for slider
                    range = ActualWidth - (2 * MARGIN) - m_slider_size;
                    // Find position
                    pos = ((m_value - m_min) * range / (m_max - m_min)) + MARGIN;
                    // Draw the slider button
                    dc.DrawRectangle(brush, pen, pos, MARGIN, m_slider_size, ActualHeight - (2 * MARGIN));
                    break;
            }
        }

        /// <summary>
        /// Gets position from StylusEventArgs and sets Value accordingly
        /// </summary>
        /// <param name="e"></param>
        void ReadPosition(Microsoft.SPOT.Input.StylusEventArgs e)
        {
            int x, y, pos, range;
            // Gets (x, y) position relative to "this"
            e.GetPosition(this, out x, out y);

            // Here comes a bunch of calculations. Pretty messy, but so far it works
            switch (DIRECTION)
            {
                case Direction.HORIZONTAL:
                    pos = x - MARGIN - (m_slider_size / 2);
                    range = ActualWidth - (2 * MARGIN) - m_slider_size;
                    if (pos > range) pos = range;
                    else if (pos < 0) pos = 0;
                    pos += (range / (m_max - m_min) / 2);   // 
                    Value = (pos * (m_max - m_min) / range) + m_min;
                    break;
                default:
                    pos = y - MARGIN - (m_slider_size / 2);
                    range = ActualHeight - (2 * MARGIN) - m_slider_size;
                    if (pos > range) pos = range;
                    else if (pos < 0) pos = 0;
                    pos += (range / (m_max - m_min) / 2);
                    Value = m_max - (pos * (m_max - m_min) / range);
                    break;
            }
        }

        bool m_selected = false;
        /// <summary>
        /// Happens when stylus is put down  on this object
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStylusDown(Microsoft.SPOT.Input.StylusEventArgs e)
        {
            base.OnStylusDown(e);
            m_selected = true;
            ReadPosition(e);
        }

        /// <summary>
        /// Happens when stylus is moved across this object
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStylusMove(Microsoft.SPOT.Input.StylusEventArgs e)
        {
            base.OnStylusMove(e);

            if (m_selected)
            {
                base.OnStylusMove(e);
                ReadPosition(e);
            }
        }

        /// <summary>
        /// Happens when stylus is lifted up from this object
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStylusUp(Microsoft.SPOT.Input.StylusEventArgs e)
        {
            base.OnStylusUp(e);

            if (m_selected)
            {
                base.OnStylusUp(e);
                ReadPosition(e);
            }
            m_selected = false;
        }
    }
}
